/*
   Copyright 2009 Bloo. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package org.youtensils.www.client;

import org.youtensils.www.client.browser.BrowserCapabilities;
import org.youtensils.www.client.data.DownloadItem;
import org.youtensils.www.client.data.youtube.YouTubeMovie;
import org.youtensils.www.client.events.MovieActionEvent;
import org.youtensils.www.client.events.MovieActionListener;
import org.youtensils.www.client.events.DownloadListener;
import org.youtensils.www.client.resources.GlobalImageBundle;
import org.youtensils.www.client.widgets.ClosableTab;
import org.youtensils.www.client.widgets.DownloadEngine;
import org.youtensils.www.client.widgets.DownloadManager;
import org.youtensils.www.client.widgets.DownloadTracker;
import org.youtensils.www.client.widgets.SearchResultsPanel;
import org.youtensils.www.client.widgets.dialogs.ErrorDialog;
import org.youtensils.www.client.widgets.dialogs.JavaRequiredDialog;
import org.youtensils.www.client.widgets.dialogs.JavaSandboxDialog;

import com.google.gwt.core.client.EntryPoint;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.event.dom.client.KeyCodes;
import com.google.gwt.event.dom.client.KeyPressEvent;
import com.google.gwt.event.dom.client.KeyPressHandler;
import com.google.gwt.event.logical.shared.ResizeEvent;
import com.google.gwt.event.logical.shared.ResizeHandler;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HTML;
import com.google.gwt.user.client.ui.HasHorizontalAlignment;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Image;
import com.google.gwt.user.client.ui.RootPanel;
import com.google.gwt.user.client.ui.TabBar;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.VerticalSplitPanel;
import com.google.gwt.user.client.ui.Widget;

/**
 * The GWT application's main class.
 * @author Bobby Soares
 */
public class Youtensils implements EntryPoint, MovieActionListener, DownloadListener {
	
	private DownloadEngine downloadEngine;
	private DownloadManager downloadManager;
	private TabPanel tabPanel;
	private VerticalPanel homePanel;
	private VerticalSplitPanel mainSplitPanel;
	private TextBox searchField;
	private Button searchButton;
	private JavaSandboxDialog javaSandboxDialog;
	private JavaRequiredDialog javaRequiredDialog;
	private ErrorDialog errorDialog;
	
	public void onModuleLoad() {
		downloadEngine = new DownloadEngine();
		downloadManager = new DownloadManager();
		searchField = new TextBox();
		searchButton = new Button();
	
		mainSplitPanel = new VerticalSplitPanel();
		mainSplitPanel.setHeight("100%");
		mainSplitPanel.setWidth("100%");
		mainSplitPanel.setSplitPosition("100%");
		tabPanel = new TabPanel();
	    tabPanel.getDeckPanel().setWidth("100%");
	    tabPanel.getDeckPanel().setHeight("100%");
	    tabPanel.setHeight("100%");
	    tabPanel.setWidth("100%");
	    tabPanel.add(makeHomeWidget(), "home");
	    tabPanel.selectTab(0);
	    tabPanel.getTabBar().setVisible(false);
		homePanel = new VerticalPanel();
	    mainSplitPanel.setTopWidget(tabPanel);
		downloadEngine.addListener(this);
		downloadManager.setWidth("100%");
		downloadManager.addListener(this);
		HorizontalPanel bottom = new HorizontalPanel();
		bottom.setWidth("100%");
		bottom.setHeight("100%");
		bottom.add(downloadManager);
		bottom.setStylePrimaryName("gwt-DownloadArea");
		mainSplitPanel.setBottomWidget(bottom);
		mainSplitPanel.setStylePrimaryName("gwt-MainSplitPanel");
		RootPanel.get().add(mainSplitPanel);
		RootPanel.get().add(this.downloadEngine);
		searchField.setFocus(true);
		
		javaSandboxDialog = new JavaSandboxDialog();
		javaRequiredDialog = new JavaRequiredDialog(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				Youtensils.this.downloadEngine.start();
			}
		});
		errorDialog = new ErrorDialog();
		Window.addResizeHandler(new ResizeHandler() {
			@Override
			public void onResize(ResizeEvent event) {
				resize();
			}
		});
	}
	
	/**
	 * Builds the Youtensils home screen.
	 * @return a VerticalPanel containing the home page elements
	 */
	private VerticalPanel makeHomeWidget(){
		homePanel = new VerticalPanel();
		homePanel.setStyleName("gwt-HomePanel");
		homePanel.setWidth("100%");
		homePanel.setHeight("100%");
		homePanel.setVerticalAlignment(HorizontalPanel.ALIGN_MIDDLE);
		homePanel.setHorizontalAlignment(HorizontalPanel.ALIGN_CENTER);
		
		VerticalPanel vPanel = new VerticalPanel();
		vPanel.setHeight("100px");
		vPanel.setVerticalAlignment(VerticalPanel.ALIGN_TOP);
		vPanel.setSpacing(20);
		vPanel.setHorizontalAlignment(HorizontalPanel.ALIGN_CENTER);
		
		HorizontalPanel inputPanel = new HorizontalPanel();
		inputPanel.setSpacing(10);
		inputPanel.setWidth("600px");
		searchField.setWidth("540px");
		searchField.setHeight("28px");
		searchField.addKeyPressHandler(new KeyPressHandler() {
			@Override
			public void onKeyPress(KeyPressEvent event) {
				if(event.getCharCode() == KeyCodes.KEY_ENTER){
					newMovieSearch();
				}
			}
		});
		inputPanel.add(searchField);
		searchButton.setText("Search");
		searchButton.setHeight("28px");
		searchButton.addClickHandler(new ClickHandler() {
			@Override
			public void onClick(ClickEvent event) {
				newMovieSearch();
			}
		});
		inputPanel.add(searchButton);
		inputPanel.setStyleName("gwt-HomePanel-SearchBox");

		HorizontalPanel examplePanel = new HorizontalPanel();
		examplePanel.setWidth("600px");
		examplePanel.setHorizontalAlignment(HorizontalPanel.ALIGN_LEFT);
		examplePanel.add(new HTML("<img src=\"images/examples.png\" /><br />Obama's acceptance speech<br />http://www.youtube.com/watch?v=123ABCDE"));
		
		GlobalImageBundle bundle = (GlobalImageBundle) GWT.create(GlobalImageBundle.class);
		Image logo = bundle.YoutensilsLogo().createImage();
		VerticalPanel logoPanel = new VerticalPanel();
		logoPanel.setWidth("600px");
		logoPanel.setHorizontalAlignment(HasHorizontalAlignment.ALIGN_LEFT);
		logoPanel.add(logo);
		logoPanel.add(new HTML("<br /><span><b>u-ten-sil</b> <i>n.</i> An instrument, implement, or container used domestically, <del>especially in a kitchen</del>.</span><br />"));
		vPanel.add(logoPanel);
		vPanel.add(inputPanel);
		vPanel.add(examplePanel);
		vPanel.add(new HTML("<br/><br />This site is Open-Source and available under the Apache License 2.0. <a href=\"http://code.google.com/p/youtensils/\" target=\"_blank\">Project Site</a> - <a href=\"http://code.google.com/p/youtensils/issues/list\" target=\"_blank\">Bug Tracker</a>."));
		
		HorizontalPanel siteInfo = new HorizontalPanel();
		siteInfo.add(new HTML("&copy; Copyright 2008 YouTensils, All Rights Reserved. <a href=\"http://www.youtube.com\">Powered by YouTube</a>.&nbsp;"));
	    vPanel.add(siteInfo);
	    homePanel.add(vPanel);
	    
	    return homePanel;
	}
	
	/**
	 * Begins the download of the next item on the download list.
	 */
	private void moveToNextDownload(){
		DownloadItem item = downloadManager.getNextDownload();
		if (item != null) {
			downloadEngine.startDownload(item.getDownloadId(), item.getFormat());
		}
	}
	/**
	 * Starts a movie search.
	 * @param query the text to search for
	 * @param author the author whose videos to search
	 */
	private void newMovieSearch(){
		String query = Youtensils.this.searchField.getText();
		if(query.equals("")) return;
		newMovieSearch(query, null);
	}
	
	/**
	 * Starts a movie search.
	 * @param query the text to search for
	 * @param author the author whose videos to search
	 */
	private void newMovieSearch(String query, String author){
		int maxTitleLength = 20;
		String title = query.length() > maxTitleLength ? query.substring(0, maxTitleLength) + "..." : query;
		String vid = isVideoUrl(query);
		if(vid != null && !vid.equals("")){
			query = vid;
			title = "Video " + vid;
		}
		if((query == null || query == "") && author != null && author != ""){
			title = "Videos by " + author;
			title = title.length() > maxTitleLength ? title.substring(0, maxTitleLength) + "..." : title;
		}
		TabBar tabBar = tabPanel.getTabBar();
		SearchResultsPanel sr = new SearchResultsPanel(query, author);
		sr.addActionListener(this);
		new ClosableTab(title, sr, tabPanel);
		tabBar.setVisible(true);
		tabBar.selectTab(tabBar.getTabCount() - 1);
		sr.load();
	}
	
	private void resize() {
		mainSplitPanel.setSplitPosition((RootPanel.get().getOffsetHeight() - downloadManager.getOffsetHeight() - 10) + "px");
	}
	
	@Override
	public void onAction(MovieActionEvent e, Object sender) {
		if(this.downloadEngine.isLocked()){
			javaSandboxDialog.center();
			return;
		}
		YouTubeMovie mov = e.getMovie();
		String act = e.getAction();
		if(act.equalsIgnoreCase("author")){
			newMovieSearch("", mov.getAuthor());
			return;
		}else if(act.equalsIgnoreCase("flv")){
			downloadManager.addDownload(mov.getId(), "FLV", mov.getTitle());
			resize();
		}else if(act.equalsIgnoreCase("mp4")){
			downloadManager.addDownload(mov.getId(), "MP4", mov.getTitle());
			resize();
		}else if(act.equalsIgnoreCase("mp3")){
			downloadManager.addDownload(mov.getId(), "MP3", mov.getTitle());
			resize();
		}
		if(!this.downloadEngine.isStarted()){
			int[] jre = BrowserCapabilities.getJavaVersion();
			if(jre[0] < 1 || jre[1] < 4){
				javaRequiredDialog.center();
			}else{
				this.downloadEngine.start();
			}
		}
		if(!this.downloadManager.hasActiveDownload()) this.moveToNextDownload();
	}
	
	@Override
	public void onDownloadProgress(Widget sender, String Id, int totalSteps, int currentStep,
			int totalBytes, int loadedBytes, String label) {
		int per = 0;
		if(totalBytes > 0) per = Math.round(loadedBytes*100/totalBytes);
		String total = "", loaded;
		total = (double)Math.round(totalBytes/1000)/1000d + "Mb";
		loaded = (double)Math.round(loadedBytes/1000)/1000d + "Mb";
		this.downloadManager.setDownloadProgressInformation(Id, per, loaded, total, "Downloading");
	}
	
	@Override
	public void onBeginDownload(Widget sender, String id) {
		DownloadTracker track = this.downloadManager.getDownloadTracker(id);
		if(track != null) {
			track.setProgressStatus(DownloadTracker.STARTING_STATUS);
		}
	}
	
	@Override
	public void onCancelDownload(Widget sender, String id) {
		if(sender == this.downloadEngine){
			DownloadTracker track = this.downloadManager.getDownloadTracker(id);
			if(track != null){
				track.setProgressStatus(DownloadTracker.CANCELED_STATUS);
				track.setProgressAction("");
			}
		    this.moveToNextDownload();
		}else if(sender == this.downloadManager){
			this.downloadEngine.cancelDownload(id);
		}
	}
	
	@Override
	public void onCompleteDownload(Widget sender, String id) {
		DownloadTracker track = this.downloadManager.getDownloadTracker(id);
		if(track != null){
			track.setProgressStatus(DownloadTracker.COMPLETED_STATUS);
			track.setProgressAction("");
		}
	    this.moveToNextDownload();
	}
	
	@Override
	public void onErrorDownload(Widget sender, String id, String error) {
		DownloadTracker track = this.downloadManager.getDownloadTracker(id);
		if(track != null){
			track.setProgressStatus(DownloadTracker.ERROR_STATUS);
			track.setProgressAction("");
		}
		errorDialog.center(error, new Runnable() {
			@Override
			public void run() {
				Youtensils.this.moveToNextDownload();
			}
		});
	}
	
	@Override
	public void onSandboxDetected(Widget sender) {
		javaSandboxDialog.center();
	}
	
	/**
	 * A native wrapper to the JavaScript alert method.
	 * @param msg the message to display in the alert prompt
	 */
	public native static void alert(String msg) /*-{
		alert(msg);
	}-*/;
	
	/**
	 * Validates a YouTube Video URL by making use of native JavaScript regex support.
	 * @param s the URL to validate
	 * @return the video id, if the URL is valid, null otherwise
	 */
	public native static String isVideoUrl(String s) /*-{
        var re = /(?:http:\/\/)?(?:www\.)?youtube\.com\/watch\?.*v=([a-zA-Z0-9_-]+).*$/gi;
        var mat = re.exec(s);
        if (!mat || mat.length < 1) mat = re.exec(s);
        if (mat && mat.length > 1) return mat[1];
        else return null;
    }-*/;
	
}